package com.boot2.core.utils;

import com.boot2.core.exception.BusinessException;
import com.boot2.core.executor.VisiableThreadPoolTaskExecutor;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.junit.Test;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author zhangweilin
 * @version V1.0
 * @date 2016年4月15日 下午3:22:07
 * @description 多线程分发工具类，关于线程数，见https://blog.csdn.net/luofenghan/article/details/78596950#%E8%AE%BE%E7%BD%AE%E6%9C%80%E5%A4%A7%E7%BA%BF%E7%A8%8B%E6%95%B0
 * 公式如下:corePoolSize(最优同时线程数量)=N(cpu)*U(cpu)*(1+W/C),其中:
 * Ncpu
 * ：表示处理器数量，可以通过Runtime.getRuntime().avaliableProcessors()获得；
 * Ucpu
 * ：CPU的使用率，0⩽Ucpu⩽1
 * ；
 * W/C
 * ：等待时间与计算时间的比值；
 */
public class ThreadPoolTaskExecutorUtil {

    private static final ThreadPoolTaskExecutor executor = createThreadPoolTaskExecutor(0.3);
    /**
     * CPU数量
     */
    public static int processorNum ;

    static {
        processorNum = Runtime.getRuntime().availableProcessors();
    }

    public static int count=0;

    /**
     *核心线程数, 系统中同时处于活跃运行状态的最大线程数量
     */
//    private static final int corePoolSize = 7;
    private static final int corePoolSize = new Double(processorNum*0.5*(1+0.5)).intValue();
    /**
     * 线程池维护线程的最大数量
     */
    private static final int maxPoolSize = 20;
    /**
     * 线程池所使用的缓冲队列
     */
    private static final int queueCapacity = 99999;

    /**
     * 线程池中的线程的名称前缀
     */
    private static final String threadNamePrefix = "ThreadPoolTaskExecutor_"+StringUtils.random(10,true,true)+"_";



    /**
     * @Fields FIX_PARAMETER : 固定的参数个数
     */
    private static final int FIX_PARAMETER_COUNT = 2;

    /**
     * @param clazz   被调用的线程实现类
     * @param list    需要执行的结果集
     * @param objects ...（可变参数）线程需要用到的参数，没有传null
     * @return void
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @description
     */
    public static void executorBlock(Class<? extends Runnable> clazz, List<?> list, Object... objects) {
        Constructor<?>[] constructors = clazz.getConstructors();
        Class<?>[] paraArr = constructors[0].getParameterTypes();

//        ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);
//        ThreadPoolExecutor exe = (ThreadPoolExecutor) pool;
        ThreadPoolTaskExecutor executor = createThreadPoolTaskExecutor(0.3);
//        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

//        System.out.println(executor.getPoolSize());
        for (int i = 0; i < list.size(); i++) {
            // 每一段的开始索引
            Runnable runnable = null;
            try {
                // 大于2个就是内部类需要传内部类的实例
                if (paraArr.length > FIX_PARAMETER_COUNT) {
                    runnable = (Runnable) constructors[0].newInstance(null, list.get(i), objects);
                } else {
                    runnable = (Runnable) constructors[0].newInstance(list.get(i), objects);
                }
            } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                e.printStackTrace();
            }
//            System.out.println("提交: "+i);
            Future<?> submit = executor.submit(runnable);
//            System.out.println("submit = " + submit.isDone());
        }
        executor.shutdown();
    }

//    public static void executeThreadPool(Class<? extends Runnable> clazz, List<?> list, Object... objects) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        ThreadPoolTaskExecutor executor = createThreadPoolTaskExecutor();
//        executeThreadPool(executor, clazz, list, objects);
//    }
//
//    public static void executeThreadPool(Class<? extends Runnable> clazz, List<?> list, int poolSize, Object... objects) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        ThreadPoolTaskExecutor executor = createThreadPoolTaskExecutor();
//        executeThreadPool(executor, clazz, list, objects);
//    }

    private static void executeThreadPool(ThreadPoolTaskExecutor threadPoolTaskExecutor, Class<? extends Runnable> clazz, List<?> list, Object... objects) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        for (int i = 0; i < list.size(); i++) {
            Runnable runnable = null;
            try {
                // call = (Callable<?>) SpringContextUtil.createBean(clazz);
                runnable = (Runnable) ConstructorUtils.invokeConstructor(clazz, list.get(i), objects);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
            Future<?> submit = threadPoolTaskExecutor.submit(runnable);
//            System.out.println("submit = " + submit);
        }
        threadPoolTaskExecutor.shutdown();
    }

    public static VisiableThreadPoolTaskExecutor createThreadPoolTaskExecutor(int corePoolSize,int maxPoolSize,int queueCapacity,String threadNamePrefix){
        VisiableThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(threadNamePrefix);

        // rejection-policy：当pool已经达到max size的时候，如何处理新任务
        // CALLER_RUNS：不在新线程中执行任务，而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
//        executor.setAllowCoreThreadTimeOut(true);
        return executor;
    }

//    public static ThreadPoolTaskExecutor createThreadPoolTaskExecutor(){
//        return createThreadPoolTaskExecutor(corePoolSize,maxPoolSize,queueCapacity,threadNamePrefix);
//    }

    /**
     * 按占用的cpu比例分配线程池大小
     * @param cpuScale 所占cpu比例，比如当前同时可能有三个线程池在跑，那么每个池大概分配30%的CPU资源，
     * @return
     */
    public static VisiableThreadPoolTaskExecutor createThreadPoolTaskExecutor(double cpuScale){
        if (cpuScale < 0 || cpuScale > 1) {
            throw new BusinessException("非法的cpu比例，该比例只能在0到1");
        }
        //公式见类注释
        int corePoolSize0 = new Double(Runtime.getRuntime().availableProcessors()*cpuScale*(1+0.5)).intValue();
        return createThreadPoolTaskExecutor(corePoolSize0,maxPoolSize,queueCapacity,threadNamePrefix);
    }

    /**
     * 按占用的cpu比例分配线程池大小
     * @param cpuScale 所占cpu比例，比如当前同时可能有三个线程池在跑，那么每个池大概分配30%的CPU资源，
     * @return
     */
    public static VisiableThreadPoolTaskExecutor createThreadPoolTaskExecutor(double cpuScale,String threadName){
        if (cpuScale < 0 || cpuScale > 1) {
            throw new BusinessException("非法的cpu比例，该比例只能在0到1");
        }
        //公式见类注释
        int corePoolSize0 = new Double(Runtime.getRuntime().availableProcessors()*cpuScale*(1+0.5)).intValue();
        return createThreadPoolTaskExecutor(corePoolSize0,maxPoolSize,queueCapacity,threadNamePrefix+threadName);
    }

    /**
     * 创建 一个窃取线程
     * workStealingPool 会自动启动cpu核数个线程去执行任务
     * @return
     */
    public static ExecutorService createWorkStealingPool() {
        return Executors.newWorkStealingPool();
    }

    /**
     * 系统自带的简单执行
     * @param runnable
     */
    public static void run(Runnable runnable) {
        executor.submit(runnable);
    }

    @Test
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(99999);
        for (int i = 0; i < 99999; i++) {
            list.add("线程_"+i);
        }
        System.out.println("list: "+list.size());
        Map<String, Object> map = new HashMap<>();
        map.put("name", "zhangsan");
        ThreadPoolTaskExecutorUtil.executorBlock(MyThread2.class, list);
    }
}

class MyThread2 implements Runnable {
    private String str;
    private Object[] objects;
    public MyThread2(String str, Object[] objects) {
        this.str = str;
        this.objects = objects;
    }

    @Override
    public void run() {
        ThreadPoolTaskExecutorUtil.count++;
        System.out.println("第"+ThreadPoolTaskExecutorUtil.count+": this.threadName: "+Thread.currentThread().getName()+ " , str" + "=" + str);
//        System.out.println(objects[0]);
//        System.out.println(objects[1]);
        // try
        // {
        // Thread.sleep(500);
        // }
        // catch (InterruptedException e)
        // {
        // // TODO Auto-generated catch block
        // e.printStackTrace();
        // }
    }

}
